package com.niiwoo.civet.trade.service.local.common;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.dangdang.ddframe.rdb.sharding.api.HintManager;
import com.dangdang.ddframe.rdb.sharding.hint.HintManagerHolder;
import com.niiwoo.activity.stage.dto.response.prize.UserPrizeResponseDTO;
import com.niiwoo.civet.trade.constant.TradeConfigRedisKey;
import com.niiwoo.civet.trade.dao.entity.Project;
import com.niiwoo.civet.trade.dao.entity.ProjectInvestCancelOrder;
import com.niiwoo.civet.trade.dao.entity.ProjectInvestOrder;
import com.niiwoo.civet.trade.dao.entity.UserEvaluationRecord;
import com.niiwoo.civet.trade.dao.mapper.ProjectInvestCancelOrderMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectInvestOrderMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectInvestorPlanMapperExt;
import com.niiwoo.civet.trade.dao.mapper.TradeConfigMapperExt;
import com.niiwoo.civet.trade.dao.mapper.UserEvaluationRecordMapperExt;
import com.niiwoo.civet.trade.enums.EvaluationLevelEnum;
import com.niiwoo.civet.trade.enums.InvestOrderCloseReasonEnum;
import com.niiwoo.civet.trade.enums.ProjectInvestOrderEvolutionStatusEnum;
import com.niiwoo.civet.trade.enums.TradeConfigEnum;
import com.niiwoo.civet.trade.service.local.plan.AverageCapitalPlusInterestAddRate;
import com.niiwoo.civet.user.enums.UserForbidTypeEnum;
import com.niiwoo.civet.user.service.UserAuthorizationInfoDubboService;
import com.niiwoo.civet.user.service.UserForbidDubboService;
import com.niiwoo.tripod.base.enums.UserTypeEnum;
import com.niiwoo.tripod.base.plan.RepaymentPlanContext;
import com.niiwoo.tripod.base.plan.support.PeriodUnitEnum;
import com.niiwoo.tripod.base.plan.support.RepaymentPlanDTO;
import com.niiwoo.tripod.base.plan.support.RepaymentTypeEnum;
import com.niiwoo.tripod.provider.exception.BizException;

import lombok.extern.slf4j.Slf4j;

/**
 * 投资服务公共组件
 * 
 * @author liuchun
 *
 *         2018年11月6日
 */
@Service
@Slf4j
public class InvestCommonServer {

	@Reference(version = "1.0.0")
	private UserAuthorizationInfoDubboService userAuthorizationInfoDubboService;
	@Reference(version = "1.0.0")
	private UserForbidDubboService userForbidDubboService;
	@Autowired
	private UserEvaluationRecordMapperExt userEvaluationRecordMapperExt;
	@Autowired
	private ProjectInvestOrderMapperExt projectInvestOrderMapperExt;
	@Autowired
	private RedisTemplate<String, String> redisTemplate;
	@Autowired
	private TradeConfigMapperExt tradeConfigMapperExt;
	@Autowired
	private ProjectInvestorPlanMapperExt projectInvestorPlanMapperExt;
	@Autowired
	private AverageCapitalPlusInterestAddRate averageCapitalPlusInterestAddRate;
	@Autowired
	private ProjectInvestCancelOrderMapperExt projectInvestCancelOrderMapperExt;
	/**
	 * 法大大授权
	 * 
	 * @param investUserType
	 * @param userId
	 */
	public void fadadaGrantAuthorization(Byte investUserType, String userId) {
		// 法大大授权，区别个人与企业
		if (UserTypeEnum.ORGANIZATION.getValue().equals(investUserType)) {
			try {
				userAuthorizationInfoDubboService.fadadaEnterAuth(userId);// 机构
			} catch (Exception e) {
				if (e instanceof BizException) {
					throw e;
				}
				throw new BizException("TRD10028");
			}
		} else {
			userAuthorizationInfoDubboService.fadadaAuth(userId);// 个人
		}
	}
	/**
	 * 投资前验证
	 * 投资金额
	 * 黑名单
	 * 风险评测
	 * 投资中订单数
	 * @param userId
	 * @param availableAmount
	 * @param investAmount
	 */
	public void investCommValidate(String userId, BigDecimal availableAmount, BigDecimal investAmount,int limit) {
		if (investAmount.compareTo(BigDecimal.ONE) < 0) {
            throw new BizException("TRD10005");
        }
        if (investAmount
                .compareTo(investAmount.setScale(0, BigDecimal.ROUND_DOWN)) != 0) {
            throw new BizException("TRD10004");
        }
    	//验证投资黑名单
    	userForbidDubboService.validUserForbidInfo(userId, UserForbidTypeEnum.INVESTOR);
    	//验证风险测评，是否做过风险测评以及风险资金限制是否上限
        vaildSurplusCanInvestAmount(userId, investAmount);
        if (availableAmount.compareTo(investAmount) < 0) {
            throw new BizException("TRD10003");// 用户余额不足，需引导用户充值
        }
        // 判断是否有5个待支付的投资订单(智能投不参加验证)
        int investingCount = projectInvestOrderMapperExt.selectInvestingCountByUserId(userId);
        if (investingCount >= limit) {
            throw new BizException("TRD30002");
        }
		
	}
	public void vaildSurplusCanInvestAmount(String userId, BigDecimal investAmout) {
        UserEvaluationRecord record = userEvaluationRecordMapperExt.selectByUserIdLimitOne(userId);
        if (record != null) {
            BigDecimal surplusCanInvestAmount = getSurplusCanInvestAmount(record);
            //判断剩余额度是否比投资金额小
            if (surplusCanInvestAmount.compareTo(investAmout) == -1) {
                //当前投资金额已达该风险类型投资上限
                throw new BizException("TRD10017");
            }
        } else {
            // 投资前请完成风险测评
            throw new BizException("TRD10002");
        }
    }
	
	/**
	 * 获取当前风险等级对应的剩余可投资金额.
	 *
	 * @param userId
	 * @return
	 */
	public BigDecimal getSurplusCanInvestAmount(String userId) {
		UserEvaluationRecord record = userEvaluationRecordMapperExt.selectByUserIdLimitOne(userId);
		if (record == null) {
			throw new BizException("TRD10002");
		}
		 
		return  getSurplusCanInvestAmount(record);
	}
	/**
	 * 获取当前风险等级对应的剩余可投资金额.
	 *
	 * @param record
	 * @return
	 */
	public BigDecimal getSurplusCanInvestAmount(UserEvaluationRecord record) {
		EvaluationLevelEnum evaluationLevelEnum = EvaluationLevelEnum.enumOf(record.getLevel());
		TradeConfigEnum tradeConfigEnum = null;
		String tradeConfigEvaluationKey = "";
		switch (evaluationLevelEnum) {
		case CONSERVATIVE:
			// 保守型
			tradeConfigEnum = TradeConfigEnum.CONSERVATIVEINVESTLIMIT;
			tradeConfigEvaluationKey = TradeConfigRedisKey.TRADE_CONFIG_CONSERVAT_IVEINVEST_LIMIT_REDIS_ROW_KEY;
			break;
		case STEADY:
			// 稳键型
			tradeConfigEnum = TradeConfigEnum.STEADINESSINVESTLIMIT;
			tradeConfigEvaluationKey = TradeConfigRedisKey.TRADE_CONFIG_STEADINESS_INVEST_LIMIT_REDIS_ROW_KEY;
			break;
		case POSITIVE:
			// 积级型
			tradeConfigEnum = TradeConfigEnum.RADICALISMINVESTLIMIT;
			tradeConfigEvaluationKey = TradeConfigRedisKey.TRADE_CONFIG_RADICALISM_INVEST_LIMIT_REDIS_ROW_KEY;
			break;
		default:
			log.info("getSurplusCanInvestAmount找不到对应的枚举类型，record:{}", record);
			break;
		}

		if (tradeConfigEnum == null) {
			throw new BizException("TRD10002");
		}

		Object objvalueForEvaluation = redisTemplate.boundHashOps(TradeConfigRedisKey.TRADE_CONFIG_EVALUATION_REDIS_KEY).get(tradeConfigEvaluationKey);
		String configValue = null;
		if (objvalueForEvaluation == null) {
			configValue = tradeConfigMapperExt.selectTradeConfigByEmun(tradeConfigEnum);
			redisTemplate.boundHashOps(TradeConfigRedisKey.TRADE_CONFIG_EVALUATION_REDIS_KEY).put(tradeConfigEvaluationKey, configValue);
		} else {
			configValue = objvalueForEvaluation.toString();
		}

		BigDecimal amount = new BigDecimal(configValue);
		// 切换到主库
		setMasterRouteOnly();
		BigDecimal totalGuaranteeIncome = projectInvestorPlanMapperExt.getDueInCorpusByUserId(record.getUserId()); // 获取投资人的待收本金
		if (totalGuaranteeIncome == null) {
			totalGuaranteeIncome = BigDecimal.ZERO;
		}
		// 切换到主库
		setMasterRouteOnly();
		BigDecimal investingAndNotFullAmount = projectInvestOrderMapperExt.selectInvestingAndNotFullAmountByUserId(record.getUserId());
		if (investingAndNotFullAmount == null) {
			investingAndNotFullAmount = BigDecimal.ZERO;
		}
		BigDecimal allInvestingAmount = totalGuaranteeIncome.add(investingAndNotFullAmount); // 已投资额度：待收本金+投资中但未满标的金额
		BigDecimal surplusCanInvestAmount = amount.subtract(allInvestingAmount).compareTo(BigDecimal.ZERO) == -1 ? BigDecimal.ZERO : amount.subtract(allInvestingAmount);
		return surplusCanInvestAmount.setScale(0, BigDecimal.ROUND_DOWN);
	}
	private void setMasterRouteOnly() {
		if (!HintManagerHolder.isMasterRouteOnly()) {
			HintManager hintManager = HintManager.getInstance();
			hintManager.setMasterRouteOnly();
		}
	}
	/**
	 * 获取加息
	 * @param project
	 * @param investAmount
	 * @param userPrizeResponseDTO
	 * @return
	 */
	public BigDecimal getAddInterest(Project project, BigDecimal investAmount, UserPrizeResponseDTO userPrizeResponseDTO) {
		BigDecimal addInterest = BigDecimal.ZERO;
		// 红包类型为8:投资加息券
		log.info("InvestService.getAddInterest:userPrizeResponseDTO={}", JSON.toJSONString(userPrizeResponseDTO));

		PeriodUnitEnum addRatePeriodUnit = PeriodUnitEnum.enumOf(project.getDeadlineUnit());
		// 时间单位(1表示月，2表示天)
		Integer addRateDeadline = project.getDeadline();

		if (userPrizeResponseDTO.getRateIncreaseType() == null || userPrizeResponseDTO.getRateIncreaseType().equals(Byte.valueOf("0"))) {
			userPrizeResponseDTO.setRateIncreaseType(addRatePeriodUnit.getValue());
		}
		log.info("InvestOrderService.getAddInterest:userPrizeResponseDTO={}", JSON.toJSONString(userPrizeResponseDTO));
		// 加息期限NumberMount如果=0不限期限加息， 时间单位(1表示月，2表示天)
		if (userPrizeResponseDTO.getNumberMount() != 0 && userPrizeResponseDTO.getRateIncreaseType() != null) {
			if (userPrizeResponseDTO.getRateIncreaseType().equals(addRatePeriodUnit.getValue())) {
				addRateDeadline = userPrizeResponseDTO.getNumberMount() < project.getDeadline() ? userPrizeResponseDTO.getNumberMount().intValue() : project.getDeadline();
			} else if (userPrizeResponseDTO.getRateIncreaseType().equals(Byte.valueOf("1")) && PeriodUnitEnum.DAY.equals(addRatePeriodUnit)) {
				// 加息券单位为月，标的期限单位为天，转换加息券单位为天
				Integer addRateDays = userPrizeResponseDTO.getNumberMount().intValue() * 30; // 加息天数
				addRateDeadline = addRateDays < project.getDeadline() ? addRateDays : project.getDeadline();
			} else if (userPrizeResponseDTO.getRateIncreaseType().equals(Byte.valueOf("2")) && PeriodUnitEnum.MONTH == addRatePeriodUnit) {
				// 加息券单位为天，标的期限单位为月，计算出标的期限天数
				Integer projectDays = project.getDeadline().intValue() * 30;// 标的期限天数
				addRateDeadline = userPrizeResponseDTO.getNumberMount() < projectDays ? userPrizeResponseDTO.getNumberMount().intValue() : project.getDeadline();
				// 标的单位为月中 如果加息天数小于标的天数，按天为单位计算加息
				addRatePeriodUnit = userPrizeResponseDTO.getNumberMount() < projectDays ? PeriodUnitEnum.DAY : PeriodUnitEnum.MONTH;
			}
		}
		// 加息为月的等额本息
		if (PeriodUnitEnum.MONTH.equals(addRatePeriodUnit) && RepaymentTypeEnum.AVERAGE_CAPITAL_PLUS_INTEREST.getValue().equals(project.getRepaymentType())) {
			addInterest = averageCapitalPlusInterestAddRate.getTotalAddInterest(investAmount, userPrizeResponseDTO.getInvestRate().multiply(BigDecimal.valueOf(0.01)), project.getDeadline(),
					addRateDeadline);
		} else {
			List<RepaymentPlanDTO> repaymentPlanDTOList = RepaymentPlanContext.getRepaymentPlan(
					PeriodUnitEnum.DAY.equals(addRatePeriodUnit) ? RepaymentTypeEnum.REPAY_PRINCIPAL_INTEREST_AT_TERM : RepaymentTypeEnum.enumOf(project.getRepaymentType()), project.getType(),
					investAmount, userPrizeResponseDTO.getInvestRate().multiply(BigDecimal.valueOf(0.01)), addRateDeadline, addRatePeriodUnit,
					project.getFullSuccessTime() == null ? new Date() : project.getFullSuccessTime());

			for (RepaymentPlanDTO planDTO : repaymentPlanDTOList) {
				addInterest = addInterest.add(planDTO.getInterest());
			}
		}

		log.info("InvestService.getAddInterest={}", addInterest);
		return addInterest;
	}
	/**
	 * 公共的订单取消操作
	 * @param investOrderId
	 * @param closeReason
	 */
	public void investCancelSuccess(Long investOrderId, InvestOrderCloseReasonEnum closeReason) {
		ProjectInvestCancelOrder record = new ProjectInvestCancelOrder();
		record.setId(investOrderId);
		record.setStatus(Byte.valueOf("1"));
		projectInvestCancelOrderMapperExt.updateByPrimaryKeySelective(record);

		ProjectInvestOrder projectInvestOrder = new ProjectInvestOrder();
		projectInvestOrder.setId(investOrderId);
		projectInvestOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.CLOSED.getValue());
		projectInvestOrder.setClosedReason(closeReason.getValue());
		projectInvestOrderMapperExt.updateByPrimaryKeySelective(projectInvestOrder);
		
	}
}
